package com.hunttown.mes.common.utils;

import com.hunttown.common.domain.Query;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @title: script代码过滤
 * @author: wangjunfu
 * @date: 2021年08月26日 10:12
 * @description:
 */
public class ScriptFilterUtils {

    /**
     * 过滤script标签
     *
     * @param htmlStr 要过滤的文本
     * @return 过滤后的文本
     */
    public static String scriptFilter(String htmlStr) {
        return scriptFilter(htmlStr, false);
    }

    /**
     * 过滤script标签
     *
     * @param htmlStr    要过滤的文本
     * @param filterHtml 是否过滤HTML
     * @return 过滤后的文本
     */
    private static String scriptFilter(String htmlStr, boolean filterHtml) {
        //非空判断
        if (StringUtils.isBlank(htmlStr)) {
            return htmlStr;
        }

        try {
            //script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script>}，必须过滤
            String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
            Pattern p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
            Matcher m_script = p_script.matcher(htmlStr);
            htmlStr = m_script.replaceAll("");

            if (filterHtml) {
                //1.定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style>}
                String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
                Pattern p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
                Matcher m_style = p_style.matcher(htmlStr);
                htmlStr = m_style.replaceAll("");

                //2.过滤html标签
                String regEx_html = "<[^>]+>";
                Pattern p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
                Matcher m_html = p_html.matcher(htmlStr);
                htmlStr = m_html.replaceAll("");
            }

            //4.既然有Script标签，把尖括号也过滤掉吧
            htmlStr = htmlStr.replace("<", "");
            htmlStr = htmlStr.replace(">", "");

        } catch (Exception e) {
            System.err.println("script filter fail: " + e.getMessage());
            return "";
        }

        return htmlStr;
    }

    /**
     * 过滤XSS代码
     *
     * @param value 需要过滤的数据
     * @return 过滤后的数据
     */
    public static Object judgeXss(Object value) {
        //NULL值跳过
        if (value == null) {
            return value;
        }

        //空值继续
        if (value.toString().equals("")) {
            return value;
        }

        //匹配XSS
        if (matchingXSS(value.toString())) {
            return null;
        } else {
            return value;
        }
    }

    /**
     * XSS过滤（DTO，这段代码是公用的）
     *
     * @param map 实体对象转换而来
     * @return TF
     */
    public static boolean xssFilterForDomain(Map<String, Object> map) {
        try {
            if (map == null) {
                return false;
            }

            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getValue() == null || entry.getValue().toString().equals("")) {
                    continue;
                }

                //是否匹配到XSS
                if (entry.getKey().toLowerCase().equals("newscontent")) {
                    //富文本
                    if (matchingXSSForRichtext(entry.getValue().toString())) {
                        return true;
                    }
                } else {
                    if (matchingXSS(entry.getValue().toString())) {
                        return true;
                    }
                }
            }

            return false;

        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     * XSS过滤（Query，这段代码是公用的）
     *
     * @param query 键值对
     * @return TF
     */
    public static boolean xssFilterForQuery(Query query) {
        try {
            String json = FastJsonUtils.toJSONString(query);
            Map<String, Object> map = FastJsonUtils.fromJSON(json, Map.class);
            if (map == null) {
                return false;
            }

            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getValue() == null || entry.getValue().toString().equals("")) {
                    continue;
                }

                //判断字段中是否存在XSS代码
                if (entry.getKey().toLowerCase().equals("newscontent") || entry.getKey().toLowerCase().equals("taskcontent")) {
                    //1.富文本匹配
                    if (matchingXSSForRichtext(entry.getValue().toString())) {
                        return true;
                    }
                } else {
                    //2.普通字段匹配
                    if (matchingXSS(entry.getValue().toString())) {
                        return true;
                    }
                }
            }

            return false;

        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     * 匹配XSS标签
     *
     * @param value 待验证字符串
     * @return TF
     */
    private static boolean matchingXSS(String value) {
        if (StringUtils.isBlank(value)) {
            return false;
        }

        //推荐使用ESAPI库来避免脚本攻击,value = ESAPI.encoder().canonicalize(value);

        // 匹配script 标签（Pattern.CASE_INSENSITIVE ...忽略大小写）
        Pattern scriptPattern = Pattern.compile("<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配src形式的表达式
        scriptPattern = Pattern.compile("src[\\s].*?=\\\"?(.*?)(\\\"|>|\\\\s+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配单个的 </script> 标签
        scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配单个的<script ...> 标签
        scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 eval(...) 形式表达式
        scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 e­xpression(...) 表达式
        scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 javascript: 表达式
        scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 vbscript:表达式
        scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 onload= 表达式
        scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        return scriptPattern.matcher(value).find();
    }

    /**
     * 匹配XSS标签（富文本）
     *
     * @param value 待验证字符串
     * @return TF
     */
    private static boolean matchingXSSForRichtext(String value) {
        if (StringUtils.isBlank(value)) {
            return false;
        }

        //推荐使用ESAPI库来避免脚本攻击,value = ESAPI.encoder().canonicalize(value);

        // 匹配script 标签
        Pattern scriptPattern = Pattern.compile("<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配单个的 </script> 标签
        scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配单个的<script ...> 标签
        scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 eval(...) 形式表达式
        scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 e­xpression(...) 表达式
        scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 javascript: 表达式
        scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 vbscript:表达式
        scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
        if (scriptPattern.matcher(value).find()) {
            return true;
        }

        // 匹配 onload= 表达式
        scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        return scriptPattern.matcher(value).find();
    }

    //移除特殊标签
    private static String cleanXSSLabel(String value) {
        value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
        value = value.replaceAll("\\(", "&#40;").replaceAll("\\)", "&#41;");
        value = value.replaceAll("'", "&#39;");
        value = value.replaceAll("[e|E][v|V][a|A][l|L]\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("[s|S][c|C][r|R][i|C][p|P][t|T]", "");
        return value;
    }

    //移除XSS标签
    private static String cleanXSS(String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        }

        //推荐使用ESAPI库来避免脚本攻击,value = ESAPI.encoder().canonicalize(value);

        // 避免script 标签
        Pattern scriptPattern = Pattern.compile("<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免src形式的表达式
        scriptPattern = Pattern.compile("src[\\s].*?=\\\"?(.*?)(\\\"|>|\\\\s+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");

        // 删除单个的 </script> 标签
        scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");

        // 删除单个的<script ...> 标签
        scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免 eval(...) 形式表达式
        scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免 e­xpression(...) 表达式
        scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免 javascript: 表达式
        scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免 vbscript:表达式
        scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");

        // 避免 onload= 表达式
        scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");

        return value;
    }

}